// 第五章 类
// 类中的字段自动带有getter方法和setter方法
// 可以用定制的getter/setter方法替换掉字段的定义，而不必修改使用类的客户端--“统一访问原则”
// 用@BeanProperty注解来生成JavaBean的getXxx/setXxx方法
// 每个类都有一个主要的构造器，这个构造器和类定义“交织”在一起。它的参数直接成为类的字段。主构造器执行类体中所有的语句
// 辅助构造器是可选的，它们叫做this.

class counter { // 类并不声明为public，一个源文件可以包含多个类，所有这些类都具有仅有可见性
private var value = 0 // 必须初始化字段
def increment {value += 1} // 默认为公有
def current = value
}

val c = new counter() // 括号可以不加
c.increment
c.current  // 如果方法定义中没加括号，则调用时也不能加，方法定义中有括号，调用时可加可不加

// 最简单的带有属性的scala类：
class Person{var age = 0}
// 编译之后会生成面向JVM的类，包含一个私有的age字段和公有的getter,setter方法
// 默认的getter方法名和属性名一样，默认的setter方法名是属性名后加_=,例如：person.age_=(89)
// 反编译class文件会发现setter方法实际上是age_$eq，因为JVM不允许=出现在方法名中。调用时可以使用age_=,也可以使用age_$eq
// 如果将age声明为private，则getter setter也是私有的
// 如果将age声明为val，则age属性是final的，没有setter方法

// 在实现属性时有四种选择：
// var foo 自动生成一个setter,一个getter
// val foo 只有一个getter
// 自已实现foo foo_=
// 自己实现foo
// 注意，不能实现只写方法，即只有setter没有getter方法的属性

// 总结：scala的属性是私有的，要么带一个getter和setter(var)，要么带一个getter(val)

// 对象私有字段
class Person{
private var age = 0
def increment { age += 1 }
def current = age
def isLess(other:Person)=age<other.age
}
// 虽说age是private的，但是在isLess这个函数中调用other.age是合法的，因为other本身也是一个Person对象，而在Person对象中调用它自身的私有属性自然是合法的。
// 如果不想这样做，可以加上private[this]关键字，意义就是只能访问本对象中的属性，而像上面这段代码中的other.age，也就是某个其他对象.value这种访问方式就不再合法了，称为对象私有
// 对于类私有的属性，会生成私有的getter setter方法（val属性只会生成getter不会生成setter，这里要注意），而对于对象私有属性，根本不会生成getter setter方法

// scala允许将访问权赋予指定的类，即private[类名],会生成辅助的getter setter方法

// Bean属性
// scala自动生成的getter和setter方法并不符合javaBean规范（var foo属性生成的getter和setter是：foo和foo_=）
// 想要生成符合javaBean规范的getter setter，使用BeanProperty注解：
import scala.beans.BeanProperty // 有些书上这个包名是scala.reflect，注意应该是scala.beans
@BeanPropery var id:Int = 100

import scala.beans.BeanProperty
class Person {
@BeanProperty var name:String = _
}

// 上面的代码会生成scala版本还有java版本的getter setter方法，一共四个，外加一个默认构造方法

// 总结字段生成的方法：
// var name  : 公有的name和name_=方法
// val name  : 公有的name方法
// private var/val name : 私有的name和name_=方法（val 没有name_=方法）
// @BeanProperty var/val name : 生成java版本的getName setName ,scala版本的name name_= (val 没有setName和name_=方法)
// private[this] var/val name : 不生成任何getter setter，用于将字段访问限制在同一个对象上调用的方法，并不常用
// private[类名] var/val name : 依赖于具体实现，将访问权赋予外部类，并不常用


// 辅助构造器
import scala.beans.BeanProperty
class Person {
var name:String = _
var age:Int = _
def this(name:String) {
this()
this.name=name
}

def this(name:String, age:Int) {
this(name)
this.age=age
}
}

// 上面的代码会生成三个构造器：this() this("name") this("name", 32)


// 主构造器：
class Person(val name:String ,val age:Int){
 // 主构造器的内容
 println("hello scala")
 def sayHello(name:String) = "hello, " + name
}
// 主构造器的参数被编译为字段，其值被初始化成构造时传入的参数。在本例中name和age成为Person类的字段
// 主构造器会执行类定义中的所有语句
// 如果类名之后没有参数，则该类具备一个无参的主构造器，这样的一个构造器只是仅仅是简单地执行类体中的所有语句而已

// 构造器参数可以是不带var和val的参数，如果此参数被方法或语句所使用，它将被升级成字段，
class Person(name:String ,age:Int){
println("hello " + name)
def sayHello = "age = " + age
}
// 上述的两个字段一个被方法使用，另一个被语句使用，都被升级成字段，
// 效果类似于private[this] val
// 否则 ，该参数将不被保存为字段，它仅仅是一个可以被主构造器中的代码访问的普通参数。

// 构造器参数总结：
name:String  : 对象私有字段，如果没有方法使用name，则没有该字段
private val/var name:String : 私有字段，私有getter setter方法
val/var name:String : 私有字段， 公有getter setter
@BeanProperty val/var name:String : 私有字段，公有的scala版本和JavaBeans版的getter setter方法
// 建议这样来看待主构造器：在Scala中，类也接受参数，就像方法一样。

// 主构造器还可以是私有的。。。
class Person private(val id:Int) {
}
// 这样用户就必须通过辅助构造器来构造Person对象了。

// 嵌套类






import scala.beans.BeanProperty

object charpter05 {
  /*
   * 5.1
   * 改进5.1节的Counter类, 让它不要在Int.MaxValue时变成负数
   */
class counter {
private var value = 0
def increment() {if (value < Int.MaxValue) value += 1}
def current = value
def set(v:Int) = value = v
}
val c = new counter
c set Int.MaxValue
c 
  或：
class counter {
var value = 0
def increment() {if (value < Int.MaxValue) value += 1}
def current = value
}

val c = new counter
c value_= Int.MaxValue
c increment()
c increment()
c current()

  
  def question1: Unit = {
    class Counter {
      private var value = 0
      def increment = if (value == Int.MaxValue) value else value += 1
      def current = value
      def set(n: Int) = value = n
    }
    val c = new Counter
    c set Int.MaxValue
    println(c.increment)
  }

  /*
   * 5.2
   * 编写一个BankAccount类, 假如deposit和withdraw方法,和一个只读 的balance属性
   */
class BankAccount() {
private var balance:Double=_
def deposit(amount:Double) ={
balance+=amount
}
def withdraw(amount:Double) = {
balance-=amount
}
def seek = balance
}




  def question2: Unit = {
    class BankAccount {
      private var _balance: Double = 0

      def deposit(n: Double) = {
        _balance += n
      }
      def withdraw(n: Double) = {
        _balance -= n
      }
      def balance() = _balance
    }
    val b = new BankAccount
    b deposit 233
    b withdraw 32
    println(b.balance)
  }

  /*
   * 5.3
   * 编写一个Time类, 假如只读属性hours和minutes, 
   * 和一个检查某一时刻是否遭遇另一时刻的方法before(other: Time): Boolean,
   * Time对象应该以new Time(hrs, min)方式构建,
   * 其中hrs小时数以军用时间格式呈现
   */
class Time(var hour:Int,var min:Int) {
def before(other:Time):Boolean = {
if (this.hour > other.hour) {
true
} else if (this.hour == other.hour && min > other.min) {
true
}
else{
false
}
}
def seek = this.hour*100 + ":"+this.min
def seek2 = this.hour*60+this.min
}





  def question3(hr: Int, min: Int): Unit = {
    class Time(hr: Int, min: Int) {
      val hour = hr
      val minute = min

      def before(t: Time): Boolean = {
        if (hour < t.hour) true else if (hour > t.hour) false else if (minute < t.minute) true else false
      }
      def <(t: Time): Boolean = before(t)
    }
    println(new Time(1, 2) < new Time(2, 1))
  }

  /*
   * 5.4
   * 重新实现前一个练习中的Time类, 将内部呈现改成自午夜起的分钟数(介于0到24*60-1之间),
   * 不要改变公有接口, 也就是说, 客户端代码不应因你的修改而收到影响
   */
  def question4(hr: Int, min: Int): Unit = {
    class Time(hr: Int, min: Int) {
      val minute = hr * 60 + min

      def before(t: Time): Boolean = {
        if (minute < t.minute) true else false
      }
      def <(t: Time): Boolean = before(t)
    }

    println(new Time(1, 2) < new Time(2, 1))
  }

  /*
   * 5.5
   * 创建一个Student类, 加入可读写的JavaBeans属性name(类型为String)和id(类型为Long),有哪些方法被生成?(用javap查看)
   * 你可以在Scala中调用JavaBeans版的getter和setter方法吗?应该这样做吗?
   */
import scala.beans.BeanProperty
class Stu {
@BeanProperty var id:Long=_
@BeanProperty var name:String=_
}



  def question5: Unit = {
    class Student {
      @BeanProperty var id = 0L
      @BeanProperty var name = ""
    }

    var s = new Student
    s setId 1
    s setName "xiaoa"
    println(s.getId + ": " + s.getName)
  }

  /*
   * 5.6
   * 在5.2的Person类中提供一个主构造器, 将负年龄转换为0
   */
class Person(var age:Int) {
if (age<0) age=0
}
  
def question6: Unit = {
    class Person(ag: Int) {
      var age = if (ag > 0) ag else 0
    }
  }

  /*
   * 5.7
   * 编写一个Person类, 其主构造器接受一个字符串, 该字符串包含名字 空格和姓,
   * 如new Person("Fred Smith")
   * 提供只读属性firstName和lastName, 主构造器参数应该是var val 还是普通参数呢? 为什么?
   */
class Person(name:String) {
val firstName = name.split(" ")(0)
val lastName  = name.split(" ")(1)
}
// 普通参数。因为有内部调用且不需要外部调用


  def question7: Unit = {
    class Person(name: String) {
      val firstName = name.split(" ")(0)
      val lastName = name.split(" ")(1)
    }
  }

  /*
   * 5.8
   * 创建一个Car类, 以只读属性对应制造商  型号名称  型号年份以及一个可读写的属性用于车牌,
   * 提供四组构造器, 每一个构造器都要求制造商和型号名称为必填,
   * 型号年份和车牌为可选,如果未填, 则型号年份设置为-1, 车牌设置为空字符串,
   * 你会选择哪一个作为你的主构造器?为什么?
   */
class Car(val manu:String,var model:String,val year:Int,var plate:String) {
def this(manu:String,model:String) = {
this(manu,model,-1,"")
}
def this(manu:String,model:String,plate:String) = {
this(manu,model,-1,plate)
}
def this(manu:String,model:String,year:Int) = {
this(manu,model,year,"")
}
//def seek = "Manufactor: " + this.manu + " ,Model: " + this.model + " ,Year: " + this.year + " ,Plate: " + this.plate
println("Manufactor: " + this.manu + " ,Model: " + this.model + " ,Year: " + this.year + " ,Plate: " + plate + " ,this.Plate: " + this.plate)
}





  def question8: Unit = {
    class Car(val producer: String, val modelName: String, val modelYear: Int = -1, var carCode: String = "") {
    }
  }

  /*
   * 5.9
   * 在Java  C#  C++ 中选择一个, 重写前一个练习, Scala相比之下精简多少?
   */
  def question9: Unit = {
    //ignore
  }

  /*
   * 5.10
   * 考虑如下类:
   * class Employee(val name:String, var salary: Double) {
   *   def this() { this("John Q. Public", 0.0) }
   * }
   * 重写该类, 使用显示的字段定义, 和一个缺省的主构造器,
   * 你更倾向于使用哪一种形式? 为什么?
   */
class Emp(val name:String ,var sal:Double) {
def this() {
this("john q. public", 0.0)
}
}

class Emp {
val name:String = ""
var sal:Double=_
def setName(name:String) = {
this.name = name
}
}

  
  def question10: Unit = {
    class Employee(val name: String = "joe", var salary: Double = 0) {

    }
  }

  def main(args: Array[String]): Unit = {
  }
}